﻿using System;

namespace TI_BLE_HCI_ClientLib.DataEntities.Advertising.ManufacturerSpecificDataEntities
{
    public class ManufacturerSpecificData_iBeacon
    {
        private AssignedNumbers.CompanyIdentifier _companyIdentifier;
        private ushort _advertisementIndicator;
        private byte[] _proximityUUID;
        private ushort _major;
        private ushort _minor;
        private sbyte _txCalibratedPower;

        public AssignedNumbers.CompanyIdentifier CompanyIdentifier
        {
            get { return _companyIdentifier; }
            set { _companyIdentifier = value; }
        }

        public ushort AdvertisementIndicator
        {
            get { return _advertisementIndicator; }
            set { _advertisementIndicator = value; }
        }

        public byte[] ProximityUUID
        {
            get { return _proximityUUID; }
            set { _proximityUUID = value; }
        }

        public ushort Major
        {
            get { return _major; }
            set { _major = value; }
        }

        public ushort Minor
        {
            get { return _minor; }
            set { _minor = value; }
        }

        public sbyte TxCalibratedPower
        {
            get { return _txCalibratedPower; }
            set { _txCalibratedPower = value; }
        }

        public bool IsAppleiBeacon
        {
            get
            {
                return _companyIdentifier == AssignedNumbers.CompanyIdentifier.Apple &&
                    _advertisementIndicator == 0x1502 &&
                    _proximityUUID[0] == 0xe2 &&
                    _proximityUUID[1] == 0xc5 &&
                    _proximityUUID[2] == 0x6d &&
                    _proximityUUID[3] == 0xb5 &&
                    _proximityUUID[4] == 0xdf &&
                    _proximityUUID[5] == 0xfb &&
                    _proximityUUID[6] == 0x48 &&
                    _proximityUUID[7] == 0xd2 &&
                    _proximityUUID[8] == 0xb0 &&
                    _proximityUUID[9] == 0x60 &&
                    _proximityUUID[10] == 0xd0 &&
                    _proximityUUID[11] == 0xf5 &&
                    _proximityUUID[12] == 0xa7 &&
                    _proximityUUID[13] == 0x10 &&
                    _proximityUUID[14] == 0x96 &&
                    _proximityUUID[15] == 0xe0;
            }
        }

        public ManufacturerSpecificData_iBeacon()
        {
        }

        public static bool CanBeCreatedFromManufacturerSpecificData(ManufacturerSpecificDataAdvertisingDataStructure manufacturerSpecificData)
        {
            try
            {
                ManufacturerSpecificData_iBeacon manufacturerSpecificData_iBeacon = CreateFromManufacturerSpecificData(manufacturerSpecificData);

                return true;
            }
            catch
            {
                return false;
            }
        }

        public byte[] ToByteArray()
        {
            var buf = new byte[25];
            byte idx = 0;

            addCompanyIdentifierBytes(buf, ref idx);
            addAdvertisementIndicator(buf, ref idx);
            addProximityUUID(buf, ref idx);
            addMajor(buf, ref idx);
            addMinor(buf, ref idx);
            addTxCalibratedPower(buf, ref idx);

            return buf;
        }

        private void addCompanyIdentifierBytes(byte[] buf, ref byte idx)
        {
            buf[idx] = (byte)((ushort)this.CompanyIdentifier & 0xFF);
            buf[idx + 1] = (byte)((((ushort)this.CompanyIdentifier) >> 8) & 0xFF);

            idx += 2;
        }

        private void addAdvertisementIndicator(byte[] buf, ref byte idx)
        {
            buf[idx] = (byte)(this.AdvertisementIndicator & 0xFF);
            buf[idx + 1] = (byte)((this.AdvertisementIndicator >> 8) & 0xFF);

            idx += 2;
        }

        private void addProximityUUID(byte[] buf, ref byte idx)
        {
            Array.Copy(this.ProximityUUID, 0, buf, idx, 16);

            idx += 16;
        }

        private void addMajor(byte[] buf, ref byte idx)
        {
            buf[idx] = (byte)((this.Major >> 8) & 0xFF);
            buf[idx+1] = (byte)(this.Major & 0xFF);

            idx += 2;
        }

        private void addMinor(byte[] buf, ref byte idx)
        {
            buf[idx] = (byte)((this.Minor >> 8) & 0xFF);
            buf[idx+1] = (byte)(this.Minor & 0xFF);
            
            idx += 2;
        }

        private void addTxCalibratedPower(byte[] buf, ref byte idx)
        {
            buf[idx] = (byte)this.TxCalibratedPower;

            idx++;
        }

        public static ManufacturerSpecificData_iBeacon CreateFromManufacturerSpecificData(ManufacturerSpecificDataAdvertisingDataStructure manufacturerSpecificData)
        {
            if (manufacturerSpecificData.Data.Length != 25) return null;

            byte[] buf = manufacturerSpecificData.Data;
            byte idx = 0;

            ManufacturerSpecificData_iBeacon manufacturerSpecificData_iBeacon = new ManufacturerSpecificData_iBeacon();

            fillCompanyIdentifier(manufacturerSpecificData_iBeacon, buf, ref idx);
            fillAdvertisementIndicator(manufacturerSpecificData_iBeacon, buf, ref idx);
            fillProximityUUID(manufacturerSpecificData_iBeacon, buf, ref idx);
            fillMajor(manufacturerSpecificData_iBeacon, buf, ref idx);
            fillMinor(manufacturerSpecificData_iBeacon, buf, ref idx);
            fillTxCalibratedPower_2complemented(manufacturerSpecificData_iBeacon, buf, ref idx);

            return manufacturerSpecificData_iBeacon;
        }

        private static void fillCompanyIdentifier(ManufacturerSpecificData_iBeacon manufacturerSpecificData_iBeacon, byte[] buf, ref byte idx)
        {
            manufacturerSpecificData_iBeacon.CompanyIdentifier = (AssignedNumbers.CompanyIdentifier)((buf[idx + 1] << 8) + buf[idx]);

            idx += 2;
        }

        private static void fillAdvertisementIndicator(ManufacturerSpecificData_iBeacon manufacturerSpecificData_iBeacon, byte[] buf, ref byte idx)
        {
            manufacturerSpecificData_iBeacon.AdvertisementIndicator = (ushort)((buf[idx + 1] << 8) + buf[idx]);

            idx += 2;
        }

        private static void fillProximityUUID(ManufacturerSpecificData_iBeacon manufacturerSpecificData_iBeacon, byte[] buf, ref byte idx)
        {
            byte[] uuid = new byte[16];

            Array.Copy(buf, idx, uuid, 0, 16);

            manufacturerSpecificData_iBeacon.ProximityUUID = uuid;

            idx += 16;
        }

        private static void fillMajor(ManufacturerSpecificData_iBeacon manufacturerSpecificData_iBeacon, byte[] buf, ref byte idx)
        {
            manufacturerSpecificData_iBeacon.Major = (ushort)((buf[idx] << 8) + buf[idx + 1]);

            idx += 2;
        }

        private static void fillMinor(ManufacturerSpecificData_iBeacon manufacturerSpecificData_iBeacon, byte[] buf, ref byte idx)
        {
            manufacturerSpecificData_iBeacon.Minor = (ushort)((buf[idx] << 8) + buf[idx + 1]);

            idx += 2;
        }

        private static void fillTxCalibratedPower_2complemented(ManufacturerSpecificData_iBeacon manufacturerSpecificData_iBeacon, byte[] buf, ref byte idx)
        {
            manufacturerSpecificData_iBeacon.TxCalibratedPower = (sbyte)buf[idx];

            idx++;
        }
    }
}